package cn.xshi.route.publisher;

import cn.hutool.core.collection.CollectionUtil;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import cn.xshi.common.util.GetClientIp;
import cn.xshi.route.util.RouteInfo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.CommandLineRunner;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import javax.annotation.PreDestroy;
import javax.annotation.Resource;
import java.net.InetSocketAddress;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
/**
 * @Desc 网关路由 Netty Server
 * @Author 邓纯杰
 * @CreateTime 2012-12-12 12:12:12
 */
@Slf4j
@Component
@Order(2)
public class GatewayNettyServer implements CommandLineRunner {

    private static final String JEHC_CLOUD_GATEWAY = "jehc-cloud-omp";//动态路由存放在OMP模块（运管平台），所以服务名采用jehc-cloud-omp

    /**
     * 线程组用于处理连接工作即主线程组
     */
    private EventLoopGroup bossGroup = new NioEventLoopGroup();

    /**
     * work 线程组用于数据处理
     */
    private EventLoopGroup workGroup = new NioEventLoopGroup();

    @Resource
    RouteInfo routeInfo;

    @Resource
    GatewayChannelUtil channelUtil;

    @Resource
    DiscoveryClient discoveryClient;

    @Override
    public void run(String... args) throws Exception {
        new Thread(new GatewayNettyServerWorker()).start();
    }

    /**
     *
     */
    class GatewayNettyServerWorker implements Runnable{
        public void run(){
            startServer();
        }
    }

    /**
     * 启动初始化Netty server端
     */
    public void startServer() {

        //采用nacos服务中自身ip
        setLocalAddress();

        InetSocketAddress socketAddress  = new InetSocketAddress(routeInfo.getHost(),routeInfo.getPort());
        ServerBootstrap bootstrap = new ServerBootstrap()
                .group(bossGroup, workGroup)
                .channel(NioServerSocketChannel.class)
                .childHandler(new GatewayServerChannelInitializer(channelUtil))
                .localAddress(socketAddress)
                .option(ChannelOption.SO_BACKLOG, 1024) //设置队列大小
                .childOption(ChannelOption.SO_KEEPALIVE, true); // 两小时内没有数据的通信时,TCP会自动发送一个活动探测数据报文
        try { //绑定端口,开始接收进来的连接
            ChannelFuture future = bootstrap.bind(socketAddress).sync();
            log.info("网关路由OMP服务器启动开始监听，Host: {},Port：{}", socketAddress.getAddress(),socketAddress.getPort());
            future.channel().closeFuture().sync();
        } catch (InterruptedException e) {
            log.error("网关路由OMP服务器 Netty server error",e.getMessage());
        } finally {
            bossGroup.shutdownGracefully(); //关闭主线程组
            workGroup.shutdownGracefully(); //关闭工作线程组
        }
    }

    @PreDestroy
    public void destory() throws InterruptedException {
        bossGroup.shutdownGracefully().sync();
        workGroup.shutdownGracefully().sync();
        log.info("关闭网关路由Netty");
    }

    /**
     * 查询动态路由所在的OMP服务物理机ip
     * @return
     */
    private void setLocalAddress(){
        try {
            Long times = 0L;
            List<ServiceInstance> serverAddressList = null;
            while (true){
                serverAddressList = discoveryClient.getInstances(JEHC_CLOUD_GATEWAY);
                if(CollectionUtil.isNotEmpty(serverAddressList) || times > 60*5){//找到注册服务或者次数超过300
                    break;
                }
                times++;
                Thread.sleep(1000);
            }
            if(CollectionUtil.isNotEmpty(serverAddressList)){
                List<String> list = new ArrayList<>();
                for(ServiceInstance serviceInstance : serverAddressList){
                    list.add(serviceInstance.getHost());
                }
                List<String> ipList = GetClientIp.getLocalEthernetIpList();
                if(CollectionUtil.isNotEmpty(ipList)){
                    List<String> allList = list.stream().filter(ipList::contains).collect(Collectors.toList());
                    routeInfo.setHost(allList.get(0));
                }
            }
        }catch (Exception e){
            log.info("查询动态路由所在的OMP服务所在ip异常：{}",e);
        }
    }
}
